#include <HgAction.h>

using namespace hg;

class ActionAbstract::AbstractAction
{
public:
	bool mIsRunning;
	bool mIsFinish;
	float mNowTime;
	float mMaxTime;

	Easing::Type mEasingType;
	std::function<void(ActionAbstract*)> mCallbackFinish;

	AbstractAction(float duration)
		: mNowTime(0.0f), mMaxTime(duration), mEasingType(Easing::linear), mIsRunning(false), mIsFinish(false){}
};
ActionAbstract::ActionAbstract(float duration) :mData(new AbstractAction(duration)) {}
ActionAbstract::ActionAbstract(const ActionAbstract& action){}
ActionAbstract& ActionAbstract::operator=(const ActionAbstract& action){ return *this; }
ActionAbstract::~ActionAbstract(){ delete mData; }
inline void ActionAbstract::start(){
	if (mData->mIsRunning == true) return;
	mData->mIsRunning = true;
	mData->mIsFinish = false;
	reset();
}
inline void ActionAbstract::stop(){
	if (mData->mIsRunning == false) return;
	mData->mIsRunning = false;
	mData->mIsFinish = true;
}
inline void ActionAbstract::reset(){}
inline void ActionAbstract::step(float dt){
	if (mData->mIsRunning == false) return;
	mData->mNowTime += dt;
	update(hgMath->easing(mData->mNowTime / mData->mMaxTime, mData->mEasingType));
	if (mData->mNowTime >= mData->mMaxTime)
		endAction();
}
inline void ActionAbstract::setEasing(const Easing::Type& type) { mData->mEasingType = type; }
inline void ActionAbstract::setTime(float t){ mData->mMaxTime = t; }
inline bool ActionAbstract::isRunning()const{ return mData->mIsRunning; }
inline bool ActionAbstract::isFinished()const{ return mData->mIsFinish; }
inline void ActionAbstract::setCallbackFinished(std::function<void(ActionAbstract*)> func){ mData->mCallbackFinish = func; }
inline bool ActionAbstract::delayTime(float dt, float t){ mData->mNowTime += dt; return mData->mNowTime < t; }
inline void ActionAbstract::endAction()
{
	mData->mNowTime = 0.0f;
	mData->mIsRunning = false;
	mData->mIsFinish = true;
	if (mData->mCallbackFinish)
		mData->mCallbackFinish(this);
}
inline void ActionAbstract::update(float dt){}

class ActionGroup::GroupAction
{
public:
	~GroupAction(){ for (auto it : mActions) delete it; }

	std::vector<ActionAbstract*> mActions;
};
ActionGroup::ActionGroup() : ActionAbstract(0.0f), mGroupData(new GroupAction){}
ActionGroup::~ActionGroup(){ delete mGroupData; }
ActionGroup::ActionGroup(const ActionGroup&) : ActionAbstract(0.0f){}
ActionGroup& ActionGroup::operator=(const ActionGroup&){ return *this; }
inline void ActionGroup::start()
{
	ActionAbstract::start();
	for (auto it = mGroupData->mActions.begin(); it != mGroupData->mActions.end(); ++it)
		(*it)->start();
}
inline void ActionGroup::stop()
{
	ActionAbstract::stop();
	for (auto it = mGroupData->mActions.begin(); it != mGroupData->mActions.end(); ++it)
		(*it)->stop();
}
inline void ActionGroup::reset()
{
	ActionAbstract::reset();
	for (auto it = mGroupData->mActions.begin(); it != mGroupData->mActions.end(); ++it)
		(*it)->reset();
}
void ActionGroup::appendAction(ActionAbstract* action)
{
	mGroupData->mActions.push_back(action);
}
void ActionGroup::removeAction(ActionAbstract* action)
{
	auto it = mGroupData->mActions.begin();
	for (it; it != mGroupData->mActions.end(); ++it)
	{
		if ((*it) == action)
		{
			(*it)->stop();
			mGroupData->mActions.erase(it);
			return;
		}
	}
}
size_t ActionGroup::countActions(){ return mGroupData->mActions.size(); }

ActionsSerial::ActionsSerial() : mCurrentActionIndex(0){}
inline void ActionsSerial::reset(){ ActionGroup::reset(); mCurrentActionIndex = 0;}
inline void ActionsSerial::step(float dt)
{
	if (!this->isRunning()) return;
	if (mGroupData->mActions.empty()) return;

	ActionAbstract* action = mGroupData->mActions[mCurrentActionIndex];
	action->step(dt);
	if (action->isFinished())
		++mCurrentActionIndex;

	if (mCurrentActionIndex == mGroupData->mActions.size())
	{
		endAction();
		mCurrentActionIndex = 0;
	}
}
const unsigned int& ActionsSerial::currentAction(){ return mCurrentActionIndex; }

ActionsParallel::ActionsParallel() : mFinishedNum(0){}
inline void ActionsParallel::reset(){ ActionGroup::reset(); mFinishedNum = 0; }
inline void ActionsParallel::step(float dt)
{
	if (!this->isRunning()) return;
	if (mGroupData->mActions.empty()) return;

	mFinishedNum = 0;
	for (auto it = mGroupData->mActions.begin(); it != mGroupData->mActions.end(); ++it)
	{
		if ((*it)->isFinished()){
			++mFinishedNum;
			continue;
		}

		(*it)->step(dt);
	}

	if (mFinishedNum == mGroupData->mActions.size())
		endAction();
}
const unsigned int& ActionsParallel::finishedNum(){ return mFinishedNum; }

ActionRepeat::ActionRepeat(ActionAbstract* action, int repeat) 
: ActionAbstract(0), mAction(action), mRepeatNum(repeat), mCurrentRepeatNum(0){}
ActionRepeat::~ActionRepeat(){ delete mAction; }

inline void ActionRepeat::start(){ ActionAbstract::start(); mAction->start(); }
inline void ActionRepeat::reset()
{
	ActionAbstract::reset();
	mAction->reset();
	mCurrentRepeatNum = 0;
}
inline void ActionRepeat::step(float dt)
{
	if (!this->isRunning()) return;
	mAction->step(dt);
	if (mAction->isFinished())
	{
		if (mRepeatNum < 0){ mAction->start(); return; }
		++mCurrentRepeatNum;

		if (mCurrentRepeatNum == mRepeatNum){ mCurrentRepeatNum = 0; endAction(); }
		else mAction->start();
	}
}
inline void ActionRepeat::stop(){ ActionAbstract::stop(); mAction->stop(); }
inline void ActionRepeat::setRepeatNum(int repeat){ mRepeatNum = repeat; }
inline int ActionRepeat::repeatNum(){ return mRepeatNum; }

ActionDelay::ActionDelay(ActionAbstract* action, float delay)
: ActionAbstract(0.0f), mAction(action), mDelayTime(delay){}
ActionDelay::~ActionDelay(){ delete mAction; }
inline void ActionDelay::reset(){ ActionAbstract::reset(); mAction->reset(); }
inline void ActionDelay::step(float dt)
{
	if (!this->isRunning()) return;
	if (this->delayTime(dt, mDelayTime)) return;
	mAction->step(dt);
	if (mAction->isFinished()) endAction();
}
inline void ActionDelay::stop(){ ActionAbstract::stop(); mAction->stop(); }
inline void ActionDelay::setDelay(float delay){ mDelayTime = delay; }
inline float ActionDelay::delay(){ return mDelayTime; }

